package net.jast.sanitizer.impl.basic;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.jast.sanitizer.Sanitizer;

public class StringSanitizer implements Sanitizer {

    private static final String DOTALL = ".*";

    private static final String BLANK = "";

    private static final String STAR = "*";

    private final Map<String, Pattern> patternList = new HashMap<String, Pattern>();

    private Print groupToPrint = Print.LAST_GROUP;

    public StringSanitizer(String... patterns) {
        for (String pattern : patterns)
            putPattern(patternList, pattern);
    }

    public StringSanitizer(Print group, String... patterns) {
        this(patterns);
        this.groupToPrint = group;
    }

    private Pattern putPattern(Map<String, Pattern> list, String pattern) {
        return list.put(pattern, Pattern.compile(DOTALL + pattern + DOTALL));
    }

    public synchronized void addPattern(String pattern) {
        if (pattern == null || pattern.isEmpty())
            throw new RuntimeException("Given pattern is empty.");

        putPattern(patternList, pattern);
    }

    public boolean matches(Object obj) {
        return getMatcher(obj.toString()) != null;
    }

    public Object sanitize(Object obj) {
        if (!(obj instanceof Throwable))
            return doSanitize(obj.toString());

        return obj;
    }

    public String sanitize(String str) {
        return doSanitize(str);
    }

    private int getDigitCount(Matcher matcher) {
        int count = 0;

        for (int i = 1; i <= matcher.groupCount(); i++)
            count += matcher.group(i).length();

        return count;
    }

    private String getLastGroupMatch(final String str, final String iterPattern) {
        final Matcher matcher = patternList.get(iterPattern).matcher(str);

        if (matcher.matches()) {
            if (matcher.groupCount() > 1)
                return getReplacementWithPrintedGroup(matcher);
            else
                return getStars(iterPattern.length()).toString();
        }

        return BLANK;
    }

    private String getReplacementWithPrintedGroup(final Matcher matcher) {
        switch (this.groupToPrint) {
        case LAST_GROUP:
            return getReplacementStars(matcher).append(matcher.group(matcher.groupCount())).toString();
        default:
            return getReplacementStars(matcher).append(matcher.group(1)).toString();
        }
    }

    private StringBuffer getReplacementStars(Matcher matcher) {
        return getStars(getDigitCount(matcher));
    }

    private String getSanitizedString(final String str, final String iterPattern) {
        return str.replaceAll(iterPattern, getLastGroupMatch(str, iterPattern));
    }

    private StringBuffer getStars(final int count) {
        final StringBuffer sb = new StringBuffer();

        for (int i = 0; i < count; i++)
            sb.append(STAR);

        return sb;
    }

    protected String doSanitize(String str) {
        final String matcher = getMatcher(str);

        if (matcher != null)
            str = getSanitizedString(str, matcher);

        return str;
    }

    private String getMatcher(String str) {
        if (str == null)
            return null;

        final Iterator<String> iter = patternList.keySet().iterator();
        while (iter.hasNext()) {
            final String next = iter.next();
            if (patternList.get(next).matcher(str).matches())
                return next;
        }

        return null;
    }
}
